package com.es.www.kyl.filter;

import cn.hutool.json.JSONUtil;
import com.es.www.kyl.constant.RedisConstants;
import com.es.www.kyl.domain.User;
import com.es.www.kyl.exception.ResException;
import com.es.www.kyl.service.JwtService;
import com.es.www.kyl.service.UserService;
import com.es.www.kyl.util.RedisUtils;
import io.jsonwebtoken.Claims;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.es.www.kyl.bean.UserContext;
import com.es.www.kyl.bean.R;
import com.es.www.kyl.constant.BusinessResultEnum;
import com.es.www.kyl.constant.WebConstants;
import com.es.www.kyl.util.UserContextHolder;
import com.es.www.kyl.util.SpringContainerHolder;

import static com.es.www.kyl.constant.BusinessResultEnum.USER_PERMISSION_DENIED;

/**
 * @Author by michangtao
 * @Date 2023/3/21 20:09
 * @Description 校验token
 */
@Slf4j(topic = "service")
public class TokenFilter extends BaseFilter implements Filter {

    @Setter
    private List<String> excludeUrls = new ArrayList<>();

    private final JwtService jwtService = SpringContainerHolder.lookUp(JwtService.class);
    private final UserService userService = SpringContainerHolder.lookUp(UserService.class);
    private final RedisUtils redisUtils = SpringContainerHolder.lookUp(RedisUtils.class);

	@Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        HttpServletResponse httpServletResponse = (HttpServletResponse) response;
        String tokenStr = httpServletRequest.getHeader(WebConstants.HEADER_AUTH);
        try {
            // 检查是否在排除列表中
            if (shouldExclude(httpServletRequest.getRequestURI())) {
                // 如果在排除列表中，直接放行
                chain.doFilter(request, response);
                return;
            }

            if (Objects.isNull(tokenStr)) {
                errorResponse(httpServletResponse, R.failResponse(BusinessResultEnum.TOKEN_NOT_EXIST));
                return;
            }
            if (Objects.nonNull(UserContextHolder.getUserContext().getUserId())) {
                chain.doFilter(request, response);
                return;
            }
            //区分后台/前台登录
            String path = httpServletRequest.getRequestURI();
            String userId;
            if (path.startsWith("/kyl_server/web")) {
                //后台登录
                userId = validateWebToken(tokenStr);
            } else {
                userId = validateAppToken(tokenStr);
            }
            //填充上下文
            doInitAppContext(userId);
            chain.doFilter(request, response);
        }catch (ResException e){
            log.error("TokenFilter doFilter error: {}", e.getMessage());
            errorResponse(httpServletResponse, R.failResponse(e.getCode(), e.getMessage()));
        } finally {
            //清除上下文
            UserContextHolder.clearUserContext();
        }
    }

    private String validateWebToken(String tokenStr) {
        String tokenKey = RedisConstants.WEB_TOKEN_KEY_SUFFIX + tokenStr;
        //验证token有效性
        if (!redisUtils.hasStringKey(tokenKey)) {
            throw new ResException(BusinessResultEnum.TOKEN_NOT_EXIST);
        }
        String userStr = redisUtils.getStringValue(tokenKey);
        UserContext userContext = JSONUtil.toBean(userStr, UserContext.class);
        User user = userService.getById(userContext.getUserId());
        if (user == null || !user.isVipOrAdmin()) {
            throw new ResException(USER_PERMISSION_DENIED);
        }
        //续期
        redisUtils.expire(tokenKey, 1800L, TimeUnit.SECONDS);
        return String.valueOf(userContext.getUserId());
    }

    private String validateAppToken(String tokenStr) {
        //验证token有效性
        jwtService.isTokenValid(tokenStr);
        return jwtService.extractClaim(tokenStr, Claims::getSubject);
    }

    private boolean shouldExclude(String requestUrl) {
        log.info("requestUrl: {}", requestUrl);
        // 移除上下文路径前缀
        String pathWithoutContext = requestUrl;
        String contextPath = "/kyl_server";

        if (pathWithoutContext.startsWith(contextPath)) {
            pathWithoutContext = pathWithoutContext.substring(contextPath.length());
        }

        // 如果路径为空，设为 "/"
        if (pathWithoutContext.isEmpty()) {
            pathWithoutContext = "/";
        }

        final String finalPath = pathWithoutContext;
        return excludeUrls.stream().anyMatch(url -> {
            boolean matches = pathMatches(finalPath, url);
            if (matches) {
                log.debug("Path {} matches exclude pattern {}", finalPath, url);
            }
            return matches;
        });
    }

    private boolean pathMatches(String requestPath, String pattern) {
        // 处理通配符匹配
        if (pattern.endsWith("*")) {
            String prefix = pattern.substring(0, pattern.length() - 1);
            return requestPath.startsWith(prefix);
        } else {
            return requestPath.equals(pattern);
        }
    }

    private void doInitAppContext(String userId) {
        final UserContext userContext = new UserContext();
        userContext.setUserId(Integer.valueOf(userId));
        User user = userService.getById(userContext.getUserId());
        if (Objects.nonNull(user)) {
            userContext.setOpenId(user.getOpenId());
            userContext.setUserRole(user.getUserRole());
        }
        UserContextHolder.setUserContext(userContext);
    }

}
